
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.nio.Buffer;
import java.nio.ByteBuffer;

import javax.imageio.ImageIO;
import javax.media.opengl.GL;
import javax.media.opengl.glu.GLU;

import com.sun.opengl.impl.mipmap.Image;
import com.sun.opengl.util.BufferUtil;
import com.sun.opengl.util.texture.Texture;
import com.sun.opengl.util.texture.TextureData;
import com.sun.opengl.util.texture.TextureIO;

public class Ground {
	GL gl;
	GLU glu;
	private int shaderprogram;
	private int vert_shader, frag_shader;
	private static float scale;
	private static float scale2;
	private static float offset;
	private int map_size;
	private static float hmap[][];
	private int[] data;
	private int road[],grass[],caustics[];
	public static int shadowMap1[],shadowMap2[],shadowMap3[],shadowMap4[];
	private int list_id,num_tex;
	private Mipmap mipmap;
	Texture boxTexture1;
	

	public static int get_shadow() {
		return shadowMap1[0];
	}
	public Ground(GL gl_, GLU glu_){
		gl=gl_;
		glu=glu_;
		num_tex = 4;
		mipmap = new Mipmap(gl,glu,num_tex);
		map_init();
		loadShader();
		CreateTexture(null);
	}
	private void map_init(){
		list_id = 0;
		map_size = 128;//256*256 bmp
		scale = 5f;//масштаб
		scale2 = 50f;//высота максимальная
		offset = map_size/2f-27f;//смещение карты относительно нуля в минус 
		road = new int[1];
		grass = new int[1];
		caustics = new int[1];
		shadowMap1 = new int[1];
		shadowMap2 = new int[1];
		shadowMap3 = new int[1];
		shadowMap4 = new int[1];
		hmap = new float[map_size][map_size];
		BufferedImage image = null;
        try { 
            URL url = this.getClass().getResource(
   				 "test.bmp");
            image = ImageIO.read(url);
        } catch (IOException e) {
        	e.printStackTrace();
        }
        for(int j = 0; j < map_size; j++) {
        	for(int i = 0; i < map_size; i++) {
        		data = getPixelData(image,j,i);
        		for(int k = 0; k < data.length; k++) {
        			hmap[j][i] += data[k]/(255f*3f); 
        		}
        		hmap[j][i] += 0.02;
        		if((i-offset)*scale >= 1 && (i-offset)*scale <= 79 && (j-offset)*scale >=1 && (j-offset)*scale <=79 ) {
    				hmap[j][i] -= 0.2;
    			}
    			if((i-offset)*scale >= 161 && (i-offset)*scale <= 239 && (j-offset)*scale >= 1 && (j-offset)*scale <= 79 ) {
    				hmap[j][i] -= 0.2;
    			}
    		//	if(i >= map_size - 5) hmap[i][j] = 0;
        	}
        }

	}
	private static int[] getPixelData(BufferedImage img, int x, int y) {
		int argb = img.getRGB(x, y);

		int rgb[] = new int[] {
		    (argb >> 16) & 0xff, //red
		    (argb >>  8) & 0xff, //green
		    (argb      ) & 0xff  //blue
		};
		return rgb;
	}
	public static float hmap_height(float I, float J) {
    	float i = ((I + offset*scale)/scale);
    	float j = ((J + offset*scale)/scale);
    	if((i-offset)*scale >= -1 && (i-offset)*scale <= 81 && (j-offset)*scale >= -1 && (j-offset)*scale <=81 ) {
    		return (hmap[(int)i][(int)j]-0.1f)*scale2;
		}
		if((i-offset)*scale >= 163 && (i-offset)*scale <= 241 && (j-offset)*scale >= -1 && (j-offset)*scale <= 81 ) {
			return (hmap[(int)i][(int)j]-0.1f)*scale2;
		}
	    float x = i - (int)i;
	    float y = j - (int)j;
	    float z = 0;
	    z += hmap[(int)i][(int)j]*(1.0f-x)*(1.0f-y)*scale2;
	    z += hmap[(int)i+1][(int)j]*(x)*(1.0f-y)*scale2;
	    z += hmap[(int)i+1][(int)j+1]*(x)*(y)*scale2;
	    z += hmap[(int)i][(int)j+1]*(1.0f-x)*(y)*scale2;
    	return z;
	}
	private void CreateTexture(Image image)
	 {
		URL imageURL= this.getClass().getResource(
				 "grass.png");
		 
		 gl.glGenTextures(1,grass, 0);
		 gl.glBindTexture(GL.GL_TEXTURE_2D, grass[0]);
		 
		 gl.glShadeModel(GL.GL_FLAT);
		 gl.glDepthFunc(GL.GL_LESS);
		 mipmap.makeImages(imageURL, 0);
		 mipmap.LoadImages(0);

		 gl.glEnable(GL.GL_TEXTURE_2D);
		 
		 URL imageURL2= this.getClass().getResource(
				 "mountain.png");
		 
		 gl.glGenTextures(1,road,0);
		 gl.glBindTexture(GL.GL_TEXTURE_2D, road[0]);
		 mipmap.makeImages(imageURL2,1);
		 mipmap.LoadImages(1);
		 
		 URL imageURL3= this.getClass().getResource(
				 "caustic.png");
		 
		 gl.glGenTextures(1,caustics,0);
		 gl.glBindTexture(GL.GL_TEXTURE_2D, caustics[0]);
		 mipmap.makeImages(imageURL3,2);
		 mipmap.LoadImages(2);
		 URL imageURL4= this.getClass().getResource(
				 "qwerty.png");
	        
		 TextureData data=null;
		 try {
			 data = TextureIO.newTextureData(
					 imageURL4.openStream(),false, "png");
			 boxTexture1 = TextureIO.newTexture(data);
		 } catch (IOException exc) {
			 exc.printStackTrace();
			 System.exit(1);
		 }       
	        
		 gl.glEnable(GL.GL_TEXTURE_2D);
		 
		 gl.glGenTextures(1,shadowMap1, 0);
		 gl.glBindTexture(GL.GL_TEXTURE_2D, shadowMap1[0]);
		 gl.glTexImage2D(GL.GL_TEXTURE_2D, 0,GL.GL_DEPTH_COMPONENT,2000,2000,0,GL.GL_DEPTH_COMPONENT,
				 GL.GL_UNSIGNED_BYTE,null);
		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_LINEAR);
		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_LINEAR);       	

		 gl.glGenTextures(1,shadowMap2, 0);
		 gl.glBindTexture(GL.GL_TEXTURE_2D, shadowMap2[0]);
		 gl.glTexImage2D(GL.GL_TEXTURE_2D, 0,GL.GL_DEPTH_COMPONENT,2000,2000,0,GL.GL_DEPTH_COMPONENT,
				 GL.GL_UNSIGNED_BYTE,null);
		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_LINEAR);
		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_LINEAR);

		 gl.glGenTextures(1,shadowMap3, 0);
		 gl.glBindTexture(GL.GL_TEXTURE_2D, shadowMap3[0]);
		 gl.glTexImage2D(GL.GL_TEXTURE_2D, 0,GL.GL_DEPTH_COMPONENT,2000,2000,0,GL.GL_DEPTH_COMPONENT,
				 GL.GL_UNSIGNED_BYTE,null);
		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_LINEAR);
		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_LINEAR);
		 
		 gl.glGenTextures(1,shadowMap4, 0);
		 gl.glBindTexture(GL.GL_TEXTURE_2D, shadowMap4[0]);
		 gl.glTexImage2D(GL.GL_TEXTURE_2D, 0,GL.GL_DEPTH_COMPONENT,2000,2000,0,GL.GL_DEPTH_COMPONENT,
				 GL.GL_UNSIGNED_BYTE,null);
		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_LINEAR);
		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_LINEAR);
	 }
	
	 private void loadShader(){
		 vert_shader = gl.glCreateShader(GL.GL_VERTEX_SHADER);
	        frag_shader = gl.glCreateShader(GL.GL_FRAGMENT_SHADER);
	        
	        BufferedReader brv = null;
			try {
				brv = new BufferedReader(
						new FileReader(
								"src/ground.vert"));
			} catch (FileNotFoundException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
	        String vsrc = "";
	        String line;
	        try {
				while ((line=brv.readLine()) != null) {
				  vsrc += line + "\n";
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        gl.glShaderSource(vert_shader, 1,
	        		new String[] {vsrc}, (int[])null, 0);
	        gl.glCompileShader(vert_shader);
	        
	        BufferedReader brf = null;
			try {
				brf = new BufferedReader(
						new FileReader(
								"src/ground.frag"));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        String fsrc = "";
	        String lines;
	        try {
				while ((lines=brf.readLine()) != null) {
				  fsrc += lines + "\n";
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        gl.glShaderSource(frag_shader, 1,
	        		new String[] {fsrc}, (int[])null, 0);
	        gl.glCompileShader(frag_shader);
	        
	        shaderprogram = gl.glCreateProgram();
	        gl.glAttachShader(shaderprogram, vert_shader);
	        gl.glAttachShader(shaderprogram, frag_shader);
	        gl.glLinkProgram(shaderprogram);
	        gl.glValidateProgram(shaderprogram);
	 }
	 private void setShader(Vector3D pos){
		 	gl.glActiveTexture(GL.GL_TEXTURE0);
		    gl.glBindTexture(GL.GL_TEXTURE_2D,road[0]);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "road"), 0);
	        
	        gl.glActiveTexture(GL.GL_TEXTURE1);
		    gl.glBindTexture(GL.GL_TEXTURE_2D,grass[0]);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "grass"), 1);
	        
	        gl.glActiveTexture(GL.GL_TEXTURE2);
		    gl.glBindTexture(GL.GL_TEXTURE_2D,caustics[0]);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "caustics"), 2);
	        
	        gl.glActiveTexture(GL.GL_TEXTURE3);
		    gl.glBindTexture(GL.GL_TEXTURE_2D,shadowMap1[0]);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "shadowMap1"), 3);
	        
	        gl.glActiveTexture(GL.GL_TEXTURE4);
		    gl.glBindTexture(GL.GL_TEXTURE_2D,shadowMap2[0]);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "shadowMap2"), 4);
	        
	        gl.glActiveTexture(GL.GL_TEXTURE5);
		    gl.glBindTexture(GL.GL_TEXTURE_2D,shadowMap3[0]);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "shadowMap3"), 5);
	        
	        gl.glActiveTexture(GL.GL_TEXTURE6);
		    gl.glBindTexture(GL.GL_TEXTURE_2D,shadowMap3[0]);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "shadowMap4"), 6);
	        
	        gl.glUniform1f(gl.glGetUniformLocation(
	        		shaderprogram, "time"), (float)(((int)System.currentTimeMillis())%1000000)/10000);
	        
	        gl.glUniform3f(gl.glGetUniformLocation(
	        		shaderprogram, "eyePos"), pos.x,pos.y,pos.z);
	        gl.glUniform3f(gl.glGetUniformLocation(
	        		shaderprogram, "lightPos"), Atmosphere.sun.x,Atmosphere.sun.y,Atmosphere.sun.z);
	        //gl.glPopMatrix();
	 }
	public static Vector3D normal(Vector3D A, Vector3D B, Vector3D C) {
	    float x = A.y()*(B.z() - C.z()) + B.y()*(C.z() - A.z()) + C.y()*(A.z() - B.z());
	    float y = A.z()*(B.x() - C.x()) + B.z()*(C.x() - A.x()) + C.z()*(A.x() - B.x());
	    float z = A.x()*(B.y() - C.y()) + B.x()*(C.y() - A.y()) + C.x()*(A.y() - B.y());
	    Vector3D result = new Vector3D(-x,-y,-z);
	    result.normalize();
	    return result;
	}
	public void shot(int width, int height, int num_map) {
		if(num_map == 1) {
			gl.glActiveTexture(GL.GL_TEXTURE3);
			gl.glBindTexture    (GL.GL_TEXTURE_2D, shadowMap1[0]);
		}
		if(num_map == 2) {
			gl.glActiveTexture(GL.GL_TEXTURE4);
			gl.glBindTexture    (GL.GL_TEXTURE_2D, shadowMap2[0]);
		}
		if(num_map == 3) {
			gl.glActiveTexture(GL.GL_TEXTURE5);
			gl.glBindTexture    (GL.GL_TEXTURE_2D, shadowMap3[0]);
		}
		if(num_map == 4) {
			gl.glActiveTexture(GL.GL_TEXTURE6);
			gl.glBindTexture    (GL.GL_TEXTURE_2D, shadowMap4[0]);
		}
    	gl.glCopyTexImage2D (GL.GL_TEXTURE_2D, 0,  GL.GL_DEPTH_COMPONENT, 0, 0, width, height, 0 );
	}
	 public void draw(Vector3D pos,float yaw, float pitch,boolean shadow) {
		 	Vector3D player_norm = new Vector3D((float)Math.cos(yaw)*(float)Math.cos(pitch),(float)Math.sin(yaw)*(float)Math.cos(pitch),(float)Math.sin(pitch));
				 gl.glUseProgram(shaderprogram);
				 setShader(pos);
				 	if(list_id != 0){			 
					    gl.glCallList(list_id);
					 }
					 else {
						 list_id=gl.glGenLists(1);
						 gl.glNewList(list_id, GL.GL_COMPILE_AND_EXECUTE);
			//	gl.glBindTexture(GL.GL_TEXTURE_2D, grass[0]);
		//	 if(shadow) gl.glDisable(GL.GL_CULL_FACE);
		 		gl.glBegin(GL.GL_QUADS);
		 		float[][] h2 = new float[3][map_size];
		 		if(shadow) {
		 			for(int i= (int)(map_size/1.5f - 2);i<map_size/1.5f;i++) {
		 				for(int j=0;j<map_size;j++) {
		 					h2[i - (int)(map_size/1.5f - 2)][j] = hmap[i][j];
		 					hmap[i][j] = 0;
		 				}
		 			}
		 		}
		 		for(int i=1;i<map_size/1.5f;i++)
			        for(int j=1;j<map_size-2;j++)
			        {

			        	int pol = (int)scale;
			            float tmpi,tmpj;
			        	float I = i*scale-offset*scale;
			        	float J = j*scale-offset*scale;
			        	
			        	Vector3D to_player = new Vector3D(pos.x - I,pos.y - J,pos.z - hmap[i][j]*scale2);
		        	  //  if(player_norm.dot(to_player) > 0) continue;//удаление всего сзади игрока
			        	
			        	    Vector3D a1 = new Vector3D();  
			        	    Vector3D a2 = new Vector3D();  
			        	    Vector3D a3 = new Vector3D();  
			        	    Vector3D norm = new Vector3D();
			        	    
			        	    a1.setXYZ(I,J+1,hmap[i][j+1]*scale);
			        	    a2.setXYZ(I+1,J-1,hmap[i+1][j-1]*scale);
			        	    a3.setXYZ(I-1,J-1,hmap[i-1][j-1]*scale);
			        	    norm = normal(a1,a2,a3);
			       // 	    if(norm.dot(player_norm) > 0) continue;//удаление всего, что смотрит в ту же сторону, что и игрок
			        	    gl.glNormal3f(norm.x,norm.y,norm.z);
			        	    gl.glTexCoord2d((float)((int)i%pol)/pol,(float)((int)(j)%pol)/pol);
			                gl.glVertex3f(I,J,hmap[i][j]*scale2);
			                
			               // if(I == 0 && J == 0) System.out.println(I + " " + J + " " + hmap[i][j]*scale2 + "\n");
			                tmpi = (float)(((int)(i)+1)%pol)/pol; if(tmpi == 0) tmpi = 1;
			                a1.setXYZ(I+1,J+1,hmap[i+1][j+1]*scale);
			        	    a2.setXYZ(I+2,J-1,hmap[i+2][j-1]*scale);
			        	    a3.setXYZ(I,J-1,hmap[i][j-1]*scale);
			        	    norm = normal(a1,a2,a3);
			        	    gl.glNormal3f(norm.x,norm.y,norm.z);
			                gl.glTexCoord2d(tmpi,(float)((int)(j)%pol)/pol);
			                gl.glVertex3f(I+scale,J,hmap[i+1][j]*scale2);
			                
			            
			                tmpi = (float)(((int)(i)+1)%pol)/pol; if(tmpi == 0) tmpi = 1;
				            tmpj = (float)(((int)(j)+1)%pol)/pol; if(tmpj == 0) tmpj = 1;
			                a1.setXYZ(I+1,J+2,hmap[i+1][j+2]*scale);
			        	    a2.setXYZ(I+2,J,hmap[i+2][j]*scale);
			        	    a3.setXYZ(I,J,hmap[i][j]*scale);
			        	    norm = normal(a1,a2,a3);
			        	    gl.glNormal3f(norm.x,norm.y,norm.z);
			                gl.glTexCoord2d(tmpi,tmpj);
			                gl.glVertex3f(I+scale,J+scale,hmap[i+1][j+1]*scale2);
			                
			                tmpj = (float)(((int)(j)+1)%pol)/pol; if(tmpj == 0) tmpj = 1;
			                a1.setXYZ(I,J+2,hmap[i][j+2]*scale);
			        	    a2.setXYZ(I+1,J,hmap[i+1][j]*scale);
			        	    a3.setXYZ(I-1,J,hmap[i-1][j]*scale);
			        	    norm = normal(a1,a2,a3);
			        	    gl.glNormal3f(norm.x,norm.y,norm.z);
			                gl.glTexCoord2d((float)((int)(i)%pol)/pol,tmpj);
			                gl.glVertex3f(I,J+scale,hmap[i][j+1]*scale2);
			        }
		 		gl.glEnd();
		 		//if(shadow) {
		 			for(int i= (int)(map_size/1.5f - 2);i<map_size/1.5f;i++) {
		 				for(int j=0;j<map_size;j++) {
		 					hmap[i][j] = h2[i - (int)(map_size/1.5f - 2)][j];
		 				}
		 			}
		 	//	}
			 		gl.glEndList();
					    }
		 		gl.glUseProgram(0);

	}
}